#region License

// This file is part of Grawlix.
// 
// Grawlix is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
// Grawlix is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with Grawlix.  If not, see
// <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Collections.Generic;
using C5;

namespace Grawlix.Services
{
	/// <summary>
	/// Creates and caches Domains, using the same assembly for
	/// each Domain.
	/// </summary>
	public class SingleAssemblyDomainFactory : IDomainFactory
	{
		/// <summary>
		/// The constraints for the Domains.
		/// </summary>
		private readonly ArrayList<Type> _constraints =
			new ArrayList<Type>();

		/// <summary>
		/// The service which creates 
		/// <see cref="IExecutionEnvironment"/> objects.
		/// </summary>
		private readonly IEnvironmentFactory _environmentFactory;

		/// <summary>
		/// The name of the assembly's governor.
		/// </summary>
		private readonly string _governor;

		/// <summary>
		/// Initializes a new instance of the 
		/// <see cref="SingleAssemblyDomainFactory"/> class.
		/// </summary>
		/// <param name="environmentFactory">The factory service which
		/// creates new environments for the Domain instances.
		/// </param>
		/// <param name="governor">The full name of the assembly's
		/// governor.</param>
		/// <param name="constraints">The constraints to impose on 
		/// <see cref="IGovernor"/> instances.</param>
		/// <exception cref="ArgumentNullException">
		/// <c>environmentFactory</c> is null.</exception>
		/// <exception cref="ArgumentNullException"><c>governor</c> is
		/// null.</exception>
		/// <exception cref="ArgumentNullException"><c>constraints</c>
		/// is null.</exception>
		/// <exception cref="ArgumentException">constraints must not be
		/// null</exception>
		/// <exception cref="ArgumentException">constraints must be
		/// interface types</exception>
		public SingleAssemblyDomainFactory(
			IEnvironmentFactory environmentFactory,
			string governor, IEnumerable<Type> constraints)
		{
			if (environmentFactory == null)
				throw new ArgumentNullException("environmentFactory");
			if (governor == null)
				throw new ArgumentNullException("governor");
			if (constraints == null)
				throw new ArgumentNullException("constraints");
			_governor = governor;
			_environmentFactory = environmentFactory;
			foreach (var constraint in constraints)
			{
				if (constraint == null)
				{
					throw new ArgumentException(
						"constraints must not be null", "constraints");
				}
				if (!constraint.IsInterface)
				{
					throw new ArgumentException(
						"constraints must be interface types", "constraints");
				}
				_constraints.Add(constraint);
			}
		}

		/// <summary>
		/// Gets the constraints for the Domains.
		/// </summary>
		/// <value>The list of interfaces a controller must implement.
		/// </value>
		public C5.IList<Type> Constraints
		{
			get { return new GuardedList<Type>(_constraints); }
		}

		/// <summary>
		/// The service which creates 
		/// <see cref="IExecutionEnvironment"/> objects.
		/// </summary>
		public IEnvironmentFactory EnvironmentFactory
		{
			get { return _environmentFactory; }
		}

		/// <summary>
		/// Gets the name of the assembly's governor.
		/// </summary>
		/// <value>The name of the governor of the assembly.</value>
		public string Governor
		{
			get { return _governor; }
		}

		/// <summary>
		/// Creates a domain with the specified name.
		/// </summary>
		/// <param name="name">The name of the domain to create.
		/// </param>
		/// <returns>A proxy to the domain.</returns>
		public ExternalProxy<Domain> Create(string name)
		{
			IExecutionEnvironment environment =
				EnvironmentFactory.Create(name);
			var domain = new ExternalProxy<Domain>(environment,
			                                       typeof(Domain),
			                                       name, _governor,
			                                       _constraints.
			                                       	ToArray());
			return domain;
		}
	}
}